package com.abc.xxx.service.impl;

import com.abc.xxx.constants.DataScopes;
import com.abc.xxx.dao.RoleMapper;
import com.abc.xxx.model.AccessibleDept;
import com.abc.xxx.model.AccessibleView;
import com.abc.xxx.model.Dept;
import com.abc.xxx.model.Role;
import com.abc.xxx.model.vo.RoleDataScope;
import com.abc.xxx.service.DeptService;
import com.abc.xxx.service.RoleService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.abc.xxx.service.AccessibleDeptService;
import com.abc.xxx.service.AccessibleViewService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Autowired
    private DeptService deptService;
    @Autowired
    private AccessibleDeptService accessibleDeptService;
    @Autowired
    private AccessibleViewService accessibleViewService;

    @Override
    public boolean isRoleNameExists(String roleName) {
        QueryWrapper<Role> w = new QueryWrapper<>();
        w.eq("name",roleName);
        return retBool(count(w));
    }

    @Override
    public boolean isRoleNameDuplicate(Integer roleId, String roleName) {
        QueryWrapper<Role> w = new QueryWrapper<>();
        w.ne("id",roleId);
        w.eq("name", roleName);
        return retBool(count(w));
    }

    @Transactional
    @Override
    public void saveRoleAndAccessibleDepts(Role role) {
        save(role);
        if (role.getDataScope() == DataScopes.CUSTOM_DEPT_DATA) {
            Integer roleId = role.getId();
            List<AccessibleDept> relations = role.getAccessibleDepts().stream()
                    .map(deptId -> new AccessibleDept(roleId, deptId))
                    .collect(Collectors.toList());
            accessibleDeptService.saveBatch(relations);
        }
    }

    @Transactional
    @Override
    public void updateRoleAndAccessibleDepts(Role role) {
        updateById(role);
        Integer roleId = role.getId();

        log.info("删除原来roleId关联的部门");
        QueryWrapper<AccessibleDept> dw = new QueryWrapper<>();
        dw.eq("role_id",roleId);
        accessibleDeptService.remove(dw);

        log.info("重新添加roleId关联的新的部门");
        List<AccessibleDept> relations = role.getAccessibleDepts().stream()
                .map(deptId -> new AccessibleDept(roleId, deptId))
                .collect(Collectors.toList());
        accessibleDeptService.saveBatch(relations);
    }

    @Transactional
    @Override
    public void deleteRole(Integer roleId) {
        removeById(roleId);
        QueryWrapper<AccessibleDept> rmAccDeptW = new QueryWrapper<>();
        rmAccDeptW.eq("role_id",roleId);
        accessibleDeptService.remove(rmAccDeptW);

        QueryWrapper<AccessibleView> rmAccViewW = new QueryWrapper<>();
        rmAccViewW.eq("role_id",roleId);
        accessibleViewService.remove(rmAccViewW);
    }

    @Override
    public Role findUserRoleDetail(Integer userDeptId, Integer userRoleId) {
        Role role = getById(userRoleId);
        if (role == null) {
            return null;
        }
        List<Integer> accessibleDepts = findUserAccessibleDepts(userDeptId, userRoleId, role.getDataScope());
        role.getAccessibleDepts().addAll(accessibleDepts);
        return role;
    }

    @Override
    public List<Integer> findUserAccessibleDepts(Integer userDeptId,Integer userRoleId, Integer userRoleDataScope) {
        // data_scope
        // 1：全部（不做任何限制）
        // 2：本人的数据
        // 3：本部门
        // 4：本部门及下级部门
        // 5：自定义数据权限（查t_accessible_dept表）
        Integer ds = userRoleDataScope;
        List<Integer> accessibleDepts = new ArrayList<>();
        if (ds == 3) {
            accessibleDepts.add(userDeptId);
        }
        if (ds == 4) {
            List<Dept> depts = deptService.findDescendantNodes(userDeptId);
            Set<Integer> deptSet = depts.stream().map(Dept::getId).collect(Collectors.toSet());
            accessibleDepts.add(userDeptId);
            accessibleDepts.addAll(deptSet);
        }
        if (ds == 5) {
            List<Integer> customAccessibleDepts = accessibleDeptService.findDeptIdsByRoleId(userRoleId);
            accessibleDepts.addAll(customAccessibleDepts);
        }
        return accessibleDepts;
    }

    @Override
    public RoleDataScope findRoleAccessibleDataScope(Integer roleId) {
        RoleDataScope result = new RoleDataScope();
        QueryWrapper<Role> w = new QueryWrapper<>();
        w.select("data_scope");
        w.eq("id",roleId);
        Integer dataScope = getObj(w, obj -> (Integer) obj);
        result.setDataScope(dataScope);
        if (dataScope==5){
            List<Integer> customAccessibleDeptIds = accessibleDeptService.findDeptIdsByRoleId(roleId);
            result.getCustomAccessibleDeptIds().addAll(customAccessibleDeptIds);
        }
        return result;
    }

    @Override
    public void assignDataScopePermissions(Integer roleId, RoleDataScope roleDataScope) {
        UpdateWrapper<Role> u = new UpdateWrapper<>();
        u.set("`data_scope`",roleDataScope.getDataScope());
        u.eq("`id`",roleId);
        update(u);
        if (DataScopes.CUSTOM_DEPT_DATA.equals(roleDataScope.getDataScope())){
            List<Integer> deptIds = roleDataScope.getCustomAccessibleDeptIds();
            List<AccessibleDept> accessibleDepts = deptIds.stream()
                    .map(deptId -> new AccessibleDept(roleId, deptId)).collect(Collectors.toList());
            accessibleDeptService.assignCustomDataScopePermissions(roleId,accessibleDepts);
        }
    }

}
